home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Archive / Games / Soundboard / CSliderControl.cp < prev    next >
Encoding:
Text File  |  2000-09-28  |  21.0 KB  |  804 lines  |  [TEXT/MMCC]

  1. /*
  2. ** File:        CSliderControl.cp
  3. ** Written by:  Tim Nufire
  4. **
  5. ** Copyright © 1990-1995 Apple Computer, Inc.
  6. ** All rights reserved. */
  7.  
  8. /*****************************************************************************/
  9. /* This is a custom slider ported by me from Kibitz to PowerPlant.             */
  10. /*****************************************************************************/
  11.  
  12.  
  13. enum {
  14.     kCapHeight = 11,
  15.     kThumbHeight = 9,
  16.     kThumbOffset = 11,
  17.     kSliderWidth = 20
  18. };
  19.  
  20. #include "CSliderControl.h"
  21.  
  22. #ifndef __RESOURCES__
  23. #include <Resources.h>
  24. #endif
  25.  
  26. #ifndef __TOOLUTILS__
  27. #include <ToolUtils.h>
  28. #endif
  29.  
  30. #ifndef __MEMORY__
  31. #include <Memory.h>
  32. #endif
  33.  
  34. #ifndef __OSUTILS__
  35. #include <OSUtils.h>
  36. #endif
  37.  
  38. #ifndef __CONTROLS__
  39. #include <Controls.h>
  40. #endif
  41.  
  42. #ifndef __GWLAYERS__
  43. #include "GWLayers.h"
  44. #endif
  45.  
  46. /*****************************************************************************/
  47.  
  48. #pragma options align=mac68k
  49.  
  50. typedef struct cdefRsrcJMP {
  51.     long    jsrInst;
  52.     long    moveInst;
  53.     short    jmpInst;
  54.     long    jmpAddress;
  55. } cdefRsrcJMP;
  56. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  57.  
  58. typedef struct thumbCntlParams {
  59.     Rect    limitRect;
  60.     Rect    slopRect;
  61.     short    axis;
  62. } thumbCntlParams;
  63.  
  64. #pragma options align=reset
  65.  
  66.  
  67. /*****************************************************************************/
  68. /* SetSliderJmpAddress()                                                      */
  69. /*                                                                             */
  70. /* This routine patches a dummy slider CDEF so that the slider code can      */
  71. /* reside inside our object. This be called once during application startup  */
  72. /* BEFORE any slider objects are created.                                     */    
  73. /*****************************************************************************/
  74.  
  75. void 
  76. CSliderControl::SetSliderJmpAddress()
  77. {
  78.     cdefRsrcJMPHndl    cdefRsrc;
  79.     static    ControlDefUPP    sliderCtlUPP = nil;
  80.  
  81.     cdefRsrc = (cdefRsrcJMPHndl)GetResource('CDEF', (sliderCntlProc / 16));
  82.     if (!sliderCtlUPP)
  83.         sliderCtlUPP = NewControlDefProc(CSliderControl::SliderCtl);
  84.     (*cdefRsrc)->jmpAddress = (long)sliderCtlUPP;
  85.     
  86.     /* Make sure that instruction caches don't kill us. */
  87.     #ifndef powerc
  88.         FlushInstructionCache(); 
  89.     #endif
  90. }
  91.  
  92. /*****************************************************************************
  93. ** SliderCtl(short varCode, ControlHandle ctl, short msg, long parm)         
  94. **                                                                             
  95. ** This is a custom slider originally from Kibitz. It expects contrlRfCon to
  96. ** contain a pointer to an CSliderControl object and will call that objects
  97. ** member functions UpdateSlider() and TrackSlider() to handle drawing and 
  98. ** tracking respectively.
  99. **
  100. ** There is a custom cdef for this code.  All it does is jump to this code.
  101. ** In addition to being a nice way to objectize the slider, there is another
  102. ** really good reason for this which is that it is possible for the cdef to 
  103. ** be re-entered! When the user clicks on a slider, the control manager locks 
  104. ** down the cdef, and then calls it. When the custom cdef returns to the control 
  105. ** manager, the cdef is unlocked. This all seems very reasonable. However, if 
  106. ** the code that tracks the slider updates another slider you have a problem.
  107. ** This is because the control manager handles the update by calling the cdef.  
  108. ** Of course, it locks it down, and then when the control manager is returned to, 
  109. ** it unlocks it.  BUT WAIT!!  We are still tracking the slider which caused the 
  110. ** update in the first place.  This is true.  It is also true that the cdef is now 
  111. ** UNLOCKED!  It isn't a good idea to rts to code that has moved.  This is why 
  112. ** the cdef jumps to the code in the application.  We never return to the 
  113. ** (potentially unlocked) cdef.  We  return straight to the control manager.  
  114. ** Ugly problem, huh?
  115. *****************************************************************************/
  116.  
  117. pascal long 
  118. CSliderControl::SliderCtl(short /*varCode*/, ControlHandle ctl, short msg, long parm)
  119. {
  120.     CSliderControl    *thisSlider;
  121.     Rect            viewRect;
  122.     thumbCntlParams    *tcp;
  123.     
  124.     thisSlider = (CSliderControl *)(*ctl)->contrlRfCon;
  125.     viewRect = (*ctl)->contrlRect;
  126.  
  127.     if (thisSlider) switch (msg) {
  128.         case initCntl:
  129.         case dispCntl:
  130.         case posCntl:
  131.             break;
  132.  
  133.         case drawCntl:
  134.             thisSlider->UpdateSlider();
  135.             break;
  136.  
  137.         case testCntl:
  138.             if ((*ctl)->contrlHilite != 255
  139.              && (*ctl)->contrlMax
  140.              && PtInRect(*(Point *)&parm, &viewRect)) {
  141.                 thisSlider->mLastClick = *((Point *)&parm);
  142.                 
  143.                 /* Everything is the thumb.  The "thumb" routine will figure
  144.                 ** out what part it is.  Since this is a very specific control,
  145.                 ** we can get away with this simplification. */
  146.                 return(kControlIndicatorPart);
  147.             }
  148.             break;
  149.  
  150.         case calcCRgns:
  151.             parm &= 0x00FFFFFF; /* 24-bit memory manager is in use */
  152.             /* fall through to calcCntlRgn */
  153.         case calcCntlRgn:
  154.         case calcThumbRgn:
  155.             RectRgn((RgnHandle)parm, &viewRect);
  156.             break;
  157.  
  158.         case thumbCntl:
  159.             tcp = (thumbCntlParams *)parm;
  160.             tcp->limitRect = viewRect;
  161.             InsetRect(&viewRect, -64, -64);
  162.             tcp->slopRect = viewRect;
  163.             tcp->axis = 2;
  164.             break;
  165.  
  166.         case dragCntl:
  167.             thisSlider->TrackSlider(thisSlider->mLastClick);
  168.             return(true);
  169.  
  170.         case autoTrack:
  171.             break;
  172.  
  173.         default:
  174.             break;
  175.     }
  176.  
  177.     return(0);
  178. }
  179.  
  180.  
  181. // ---------------------------------------------------------------------------
  182. //        • CreateFromCNTL [static]
  183. // ---------------------------------------------------------------------------
  184. //    Create a StdControl or SliderControl from a CNTL resource
  185. /*
  186. LStdControl*
  187. LStdControl::CreateFromCNTL(
  188.     ResIDT        inCNTLid,
  189.     MessageT    inValueMessage,
  190.     ResIDT        inTextTraitsID,
  191.     LView        *inSuperView)
  192. {
  193.     LStdControl    *theStdControl = nil;
  194.  
  195.     ::HidePen();
  196.     inSuperView->FocusDraw();
  197.     
  198.     if (inTextTraitsID != 0) {        // Control does not use System font
  199.         UTextTraits::SetPortTextTraits(inTextTraitsID);
  200.     }
  201.     
  202.     ControlHandle    macControlH = ::GetNewControl(inCNTLid,
  203.                                                 inSuperView->GetMacPort());
  204.     ::ShowPen();
  205.     ThrowIfNil_(macControlH);
  206.     
  207.     SCNTLResource    *resP = *(SCNTLResourceH) ::GetResource('CNTL', inCNTLid);
  208.     
  209.     SPaneInfo    thePaneInfo;
  210.     thePaneInfo.paneID = inCNTLid;
  211.     thePaneInfo.left = resP->bounds.left;
  212.     thePaneInfo.top = resP->bounds.top;
  213.     thePaneInfo.width = resP->bounds.right - thePaneInfo.left;
  214.     thePaneInfo.height = resP->bounds.bottom - thePaneInfo.top;
  215.     thePaneInfo.visible = (resP->visible != 0);
  216.     thePaneInfo.enabled = true;
  217.     thePaneInfo.bindings.left =
  218.         thePaneInfo.bindings.top =
  219.         thePaneInfo.bindings.right =
  220.         thePaneInfo.bindings.bottom = false;
  221.     thePaneInfo.userCon = 0;
  222.     thePaneInfo.superView = inSuperView;
  223.     
  224.                                     // Mask off useWFont variation code
  225.     Int16    controlKind =
  226.             (resP->procID & ~((Uint16) kControlUsesOwningWindowsFontVariant));
  227.     switch (controlKind) {
  228.     
  229.         case pushButProc:
  230.             theStdControl = new LStdButton(thePaneInfo, inValueMessage,
  231.                                     inTextTraitsID, macControlH);
  232.             break;
  233.             
  234.         case checkBoxProc:
  235.             theStdControl = new LStdCheckBox(thePaneInfo, inValueMessage,
  236.                                     resP->value, inTextTraitsID, macControlH);
  237.             break;
  238.             
  239.         case radioButProc:
  240.             theStdControl = new LStdRadioButton(thePaneInfo, inValueMessage,
  241.                                     resP->value, inTextTraitsID, macControlH);
  242.             break;
  243.             
  244.         case popupMenuProc:
  245.             theStdControl = new LStdPopupMenu(thePaneInfo, inValueMessage,
  246.                                     ::GetControlMaximum(macControlH),
  247.                                     inTextTraitsID, macControlH);
  248.             break;
  249.             
  250.         case sliderCntlProc:
  251.             theStdControl = new CSliderControl(thePaneInfo, inValueMessage,
  252.                                     ::GetControlValue(macControlH),
  253.                                     ::GetControlMinimum(macControlH),
  254.                                     ::GetControlMaximum(macControlH),
  255.                                     controlKind, inTextTraitsID,
  256.                                     macControlH);
  257.             break;
  258.             
  259.         default:
  260.             theStdControl = new LStdControl(thePaneInfo, inValueMessage,
  261.                                     ::GetControlValue(macControlH),
  262.                                     ::GetControlMinimum(macControlH),
  263.                                     ::GetControlMaximum(macControlH),
  264.                                     controlKind, inTextTraitsID,
  265.                                     macControlH);
  266.             break;
  267.     }
  268.  
  269.     return theStdControl;
  270. }
  271. */
  272.  
  273. // ---------------------------------------------------------------------------
  274. //        • CreateSliderControlStream [static]
  275. // ---------------------------------------------------------------------------
  276. //    Create a new SliderControl from the data in a Stream
  277. //
  278. //    Current port must be the Window into which to install the control
  279.  
  280. CSliderControl*
  281. CSliderControl::CreateSliderControlStream(
  282.     LStream    *inStream)
  283. {
  284.     return (new CSliderControl(inStream));
  285. }
  286.  
  287. // ---------------------------------------------------------------------------
  288. //        • InitSlider
  289. // ---------------------------------------------------------------------------
  290. //    Initialization routine.
  291.  
  292. void
  293. CSliderControl::InitSlider(void)
  294. {
  295.     if (mMacControlH) {
  296.         SetSliderRefCon(GetControlReference(mMacControlH));
  297.         SetControlReference(mMacControlH, (long)this);
  298.     }
  299. }
  300.  
  301. // ---------------------------------------------------------------------------
  302. //        • CSliderControl(const CSliderControl&)
  303. // ---------------------------------------------------------------------------
  304. //    Copy Constructor
  305.  
  306. CSliderControl::CSliderControl(
  307.     const CSliderControl &inOriginal)
  308.         : LStdControl(inOriginal)
  309. {
  310.     InitSlider();
  311. }
  312.  
  313.  
  314. // ---------------------------------------------------------------------------
  315. //        • CSliderControl(Int16)
  316. // ---------------------------------------------------------------------------
  317. //    Construct a SliderControl for a particular kind of Toolbox Control
  318. //
  319. //    NOTE: On entry, the current Port must be Window into which to
  320. //    install the Control.
  321.  
  322. CSliderControl::CSliderControl( Int16 inControlKind) : LStdControl(inControlKind)
  323. {
  324.     InitSlider();
  325. }
  326.  
  327.  
  328. // ---------------------------------------------------------------------------
  329. //        • CSliderControl
  330. // ---------------------------------------------------------------------------
  331. //    Construct SliderControl from input parameters
  332.  
  333. CSliderControl::CSliderControl(
  334.     const SPaneInfo    &inPaneInfo,
  335.     MessageT        inValueMessage,
  336.     Int32            inValue,
  337.     Int32            inMinValue,
  338.     Int32            inMaxValue,
  339.     Int16            inControlKind,
  340.     ResIDT            inTextTraitsID,
  341.     Str255            inTitle,
  342.     Int32            inMacRefCon)
  343.         : LStdControl(inPaneInfo, inValueMessage, inValue,
  344.                         inMinValue, inMaxValue, inControlKind, 
  345.                         inTextTraitsID, inTitle, inMacRefCon)
  346. {
  347.     InitSlider();
  348. }
  349.  
  350.  
  351. // ---------------------------------------------------------------------------
  352. //        • CSliderControl
  353. // ---------------------------------------------------------------------------
  354. //    Construct from input parameters and an existing ControlHandle
  355.  
  356. CSliderControl::CSliderControl(
  357.     const SPaneInfo    &inPaneInfo,
  358.     MessageT        inValueMessage,
  359.     Int32            inValue,
  360.     Int32            inMinValue,
  361.     Int32            inMaxValue,
  362.     Int16            inControlKind,
  363.     ResIDT            inTextTraitsID,
  364.     ControlHandle    inMacControlH)
  365.         : LStdControl(inPaneInfo, inValueMessage, inValue,
  366.                         inMinValue, inMaxValue, inControlKind,
  367.                         inTextTraitsID, inMacControlH)
  368. {
  369.     InitSlider();
  370. }
  371.  
  372.  
  373. // ---------------------------------------------------------------------------
  374. //        • CSliderControl(LStream*)
  375. // ---------------------------------------------------------------------------
  376. //    Construct SliderControl from a data stream
  377.  
  378. CSliderControl::CSliderControl(
  379.     LStream    *inStream)
  380.         : LStdControl(inStream)
  381. {
  382.     InitSlider();
  383. }
  384.  
  385.  
  386.  
  387. // ---------------------------------------------------------------------------
  388. //        • ~CSliderControl
  389. // ---------------------------------------------------------------------------
  390. //    Destructor
  391.  
  392. CSliderControl::~CSliderControl()
  393. {
  394. }
  395.  
  396.  
  397. /*****************************************************************************/
  398.  
  399.  
  400.  
  401. void    
  402. CSliderControl::AdjustSlider(Int32 val, Int32 max)
  403. {
  404.     GrafPtr    oldPort = UQDGlobals::GetCurrentPort();
  405.  
  406.     SetPort((**mMacControlH).contrlOwner);
  407.  
  408.     /* Change the slider value and show the result. */
  409.     SetMaxValue(max);
  410.     SetValue(val);
  411.     UpdateSlider();
  412.  
  413.     SetPort(oldPort);
  414. }
  415.  
  416.  
  417.  
  418.  
  419.  
  420. void    
  421. CSliderControl::UpdateSlider(short hiliteCap)
  422. {
  423.     Rect        ctlRect, workRect, sliderRect;
  424.     Boolean        active;
  425.     short        i, j;
  426.     RgnHandle    origClipRgn, clipRgn, workRgn;
  427.     CIconHandle    icons[7];
  428.  
  429.     /* We use color icons here for the various slider parts.  This is so that we
  430.     ** can take advantage of the depth of monitors.  I use icons here so that I
  431.     ** can do a single plot of an icon if the delta of the thumb is -12 to 12.
  432.     ** (The thumb is in the center of an icon, and 12 pixels above and below the
  433.     ** icon, I have slider bar.  Use RedEdit to check it out.)  This technique
  434.     ** gives a very smooth appearance when the thumb slides.  There is no flash.
  435.     ** For deltas greater than +-12, I redraw the slider without the thumb, and
  436.     ** then draw the thumb in the new position.  Since the thumb is moving a lot
  437.     ** anyway, this doesn't show up as a flicker.  There is no overlap in the
  438.     ** old and new positions for a big delta. */
  439.  
  440.     FocusDraw();
  441.  
  442.     ctlRect = (*mMacControlH)->contrlRect;
  443.  
  444.     for (i = 0; i < 7; i++) icons[i] = ReadCIcon(i + rSliderBase);
  445.  
  446.     origClipRgn = NewRgn();
  447.     GetClip(origClipRgn);
  448.  
  449.     clipRgn = NewRgn();
  450.  
  451.     for (i = 0; i < 2; i++) {        /* Draw the arrow parts first. */
  452.         j = i;
  453.         if (hiliteCap == i) j += 5;
  454.         workRect = ctlRect;
  455.         if (!i)
  456.             workRect.bottom = workRect.top + kCapHeight;
  457.         else
  458.             workRect.top = workRect.bottom - kCapHeight;
  459.         RectRgn(clipRgn, &workRect);
  460.         SetClip(clipRgn);
  461.             /* Clip out the area outside the arrow part. */
  462.         SliderDrawCIcon(icons[j], workRect.left, workRect.top);
  463.             /* Draw the arrow part. */
  464.     }
  465.  
  466.     ctlRect.top    += kCapHeight;
  467.     ctlRect.bottom -= kCapHeight;
  468.     RectRgn(clipRgn, &ctlRect);
  469.     SetClip(clipRgn);
  470.         /* Clip out everything except the slider bar area. */
  471.  
  472.     active  = true; /* ((*mMacControlH)->contrlOwner == FrontWindow()); */
  473.     if ((*mMacControlH)->contrlHilite == 255) active = false;
  474.     if (!(*mMacControlH)->contrlMax) active = false;
  475.  
  476.     if (active) {        /* If control active, draw the thumb. */
  477.         sliderRect = CalcSliderRect();
  478.         SliderDrawCIcon(icons[3],
  479.                        sliderRect.left, sliderRect.top - kThumbOffset);
  480.         workRgn = NewRgn();
  481.         RectRgn(workRgn, &sliderRect);
  482.         DiffRgn(clipRgn, workRgn, clipRgn);
  483.         SetClip(clipRgn);
  484.         DisposeRgn(workRgn);
  485.             /* Now that the thumb is drawn, protect it by clipping it out. */
  486.     }
  487.  
  488.     for (i = ctlRect.top; i < ctlRect.bottom; i += 32)
  489.         SliderDrawCIcon(icons[2], ctlRect.left, i);
  490.             /* Draw the slider bar portion. */
  491.  
  492.     /* It is now completely drawn.  Clean up and get out. */
  493.  
  494.     SetClip(origClipRgn);
  495.     DisposeRgn(clipRgn);
  496.     DisposeRgn(origClipRgn);
  497.  
  498.     for (i = 0; i < 7; i++) KillCIcon(icons[i]);
  499. }
  500.  
  501.  
  502.  
  503. /*****************************************************************************/
  504.  
  505.  
  506.  
  507. void    
  508. CSliderControl::SliderDrawCIcon(CIconHandle iconHndl, short hloc, short vloc)
  509. {
  510.     Rect    iconRect;
  511.  
  512.     iconRect.right  = (iconRect.left = hloc) + 32;
  513.     iconRect.bottom = (iconRect.top  = vloc) + 32;
  514.  
  515.     DrawCIcon(iconHndl, iconRect);
  516. }
  517.  
  518.  
  519.  
  520. /*****************************************************************************/
  521.  
  522.  
  523.  
  524. Rect    
  525. CSliderControl::CalcSliderRect()
  526. {
  527.     Rect    ctlRect, sliderRect;
  528.     short    max, val;
  529.     long    calc;
  530.  
  531.     ctlRect = (*mMacControlH)->contrlRect;
  532.     ctlRect.top    += kCapHeight;
  533.     ctlRect.bottom -= kCapHeight;
  534.     max = (*mMacControlH)->contrlMax;
  535.     val = (*mMacControlH)->contrlValue;
  536.  
  537.     calc = ctlRect.bottom - ctlRect.top - kThumbHeight;
  538.     calc *= val;
  539.     if (max) calc /= max;
  540.     sliderRect.top    = ctlRect.top + calc,
  541.     sliderRect.left   = ctlRect.left;
  542.     sliderRect.bottom = sliderRect.top + kThumbHeight;
  543.     sliderRect.right  = ctlRect.right;
  544.  
  545.     return(sliderRect);
  546. }
  547.  
  548.  
  549. /*****************************************************************************/
  550.  
  551.  
  552.  
  553. short    
  554. CSliderControl::CalcSliderValue(Point mouseLoc)
  555. {
  556.     Rect    ctlRect;
  557.     long    max, val;
  558.  
  559.     ctlRect = (*mMacControlH)->contrlRect;
  560.     ctlRect.top    += kCapHeight;
  561.     ctlRect.bottom -= kCapHeight;
  562.     max = (*mMacControlH)->contrlMax;
  563.     
  564.     val = (mouseLoc.v - ctlRect.top - kThumbHeight/2) * max;
  565.     val /= ctlRect.bottom - ctlRect.top - kThumbHeight;
  566.  
  567.     if (val < 0)     val = 0;
  568.     if (val > max)    val = max;
  569.     
  570.     return(val);
  571. }
  572.  
  573.  
  574.  
  575. /*****************************************************************************/
  576.  
  577. void    
  578. CSliderControl::TrackAndRun(Point *mouseLoc)
  579. {
  580.     EventRecord    macEvent;
  581.  
  582.     // Give the movies some time (both in our app and outside)
  583.     ::WaitNextEvent (0, &macEvent, 0, 0);
  584.     
  585.     // Repeaters get time after every event
  586.     LPeriodical::DevoteTimeToRepeaters(macEvent);
  587.  
  588. //    SystemTask();
  589. //    MoviesTask(0, 0);
  590.  
  591.     FocusDraw();
  592.     *mouseLoc = macEvent.where;
  593.     GlobalToLocal(mouseLoc);
  594. }
  595.  
  596. /*****************************************************************************/
  597.  
  598. void    
  599. CSliderControl::TrackSlider(Point origMouseLoc)
  600. {
  601.     CIconHandle    icons[7];
  602.     GrafPtr        oldPort = UQDGlobals::GetCurrentPort();
  603.     Rect        ctlRect, sliderRange, slopRect, sliderRect, capRect, pgRect;
  604.     RgnHandle    origClipRgn, clipRgn, workRgn;
  605.     short        i, max, val, ovloc, voffset, vloc, delta, hiliteCap;
  606.     Boolean        hiliteOn;
  607.     long        origTick, calc;
  608.     Point        lastMouseLoc, mouseLoc;
  609.  
  610.     /* Get everything we need set up. */
  611.  
  612.     FocusDraw();
  613.     origTick = TickCount();
  614.  
  615.     for (i = 2; i < 5; i++) icons[i] = ReadCIcon(i + rSliderBase);
  616.  
  617.     SetPort((**mMacControlH).contrlOwner);
  618.     ctlRect = (*mMacControlH)->contrlRect;
  619.  
  620.     origClipRgn = NewRgn();
  621.     GetClip(origClipRgn);
  622.  
  623.     clipRgn = NewRgn();
  624.     workRgn = NewRgn();
  625.  
  626.     max = (*mMacControlH)->contrlMax;
  627.  
  628.     pgRect = ctlRect;
  629.     pgRect.top    += kCapHeight;
  630.     pgRect.bottom -= kCapHeight - 1;
  631.  
  632.     /* That ought to be enough setup. */
  633.  
  634.     if (PtInRect(origMouseLoc, &pgRect)) {        /* If in the slide area... */
  635.  
  636.         sliderRect = CalcSliderRect();
  637.         
  638.         if (!PtInRect(origMouseLoc, &sliderRect)) {    /* If they are not on the thumb... */
  639.         
  640.             /* Move the thumb to them... */
  641.             (*mMacControlH)->contrlValue = val = CalcSliderValue(origMouseLoc);
  642.             UpdateSlider();
  643.             SliderAction(val);
  644.             
  645.             /* Update starting slider rect... */
  646.             sliderRect = CalcSliderRect();
  647.         }
  648.     
  649.         RectRgn(clipRgn, &pgRect);
  650.         SetClip(clipRgn);
  651.  
  652.         sliderRange = pgRect;                    /* Calc area thumb can move. */
  653.         sliderRange.bottom -= kThumbHeight + 1;    /* Count height of thumb against range. */
  654.  
  655.         slopRect = sliderRange;                    /* Give the user some slop. */
  656.         InsetRect(&slopRect, -20, -20);
  657.  
  658.         lastMouseLoc = origMouseLoc;
  659.  
  660.         voffset = lastMouseLoc.v - sliderRect.top;
  661.         ovloc   = lastMouseLoc.v - voffset;
  662.     
  663.         while (StillDown()) {
  664.  
  665.             TrackAndRun(&mouseLoc);
  666.             SetClip(clipRgn);
  667.             if (!EqualPt(mouseLoc, lastMouseLoc)) {        /* The mouse has moved. */
  668.  
  669.                 if (!PtInRect(mouseLoc, &slopRect)) mouseLoc = lastMouseLoc;
  670.                     /* Outside slopRect, so snap back to the last good position. */
  671.  
  672.                 vloc = mouseLoc.v - voffset;
  673.                 if (vloc < sliderRange.top)    vloc = sliderRange.top;
  674.                 if (vloc > sliderRange.bottom) vloc = sliderRange.bottom;
  675.                 delta = vloc - ovloc;
  676.                     /* The delta tells us how much the thumb moved. */
  677.  
  678.                 if (
  679.                     (delta < -((32 - kThumbHeight) / 2)) ||
  680.                     (delta >  ((32 - kThumbHeight) / 2))
  681.                 ) {
  682.                     for (i = pgRect.top; i < pgRect.bottom; i += 32)
  683.                         SliderDrawCIcon(icons[2], pgRect.left, i);
  684.                             /* The thumb moved too far for a single plot to cover
  685.                             ** up the old position, so clear the old thumb. */
  686.                 }
  687.  
  688.                 calc  = max + 1;        /* Force below math to be with longs. */
  689.                 calc *= (vloc - sliderRange.top);
  690.                     /* We use max + 1 because there is one more game
  691.                     ** move position than moves in the game.  This is
  692.                     ** because we can position in front of the first move,
  693.                     ** as well as after the last move. */
  694.                 calc /= (sliderRange.bottom - sliderRange.top);
  695.  
  696.                 val = calc;
  697.                 if (val > max) val = max;
  698.                 if (val < 0)   val = 0;
  699.                 if (delta)
  700.                     SliderDrawCIcon(icons[3], pgRect.left, vloc - kThumbOffset);
  701.                                         /* The thumb is now updated. */
  702.  
  703.                 SetClip(origClipRgn);
  704.                 SliderAction(val);
  705.                     /* We set the clipRgn back to the original so the board
  706.                     ** can update.  (Pretty boring if it doesn't). */
  707.  
  708.                 SetClip(clipRgn);
  709.                 FocusDraw();
  710.                     /* Back to our normally scheduled program... */
  711.  
  712.                 lastMouseLoc = mouseLoc;
  713.                 (*mMacControlH)->contrlValue = val;
  714.                 ovloc = vloc;
  715.             }
  716.         }
  717.     }
  718.     else {        /* We missed the thumb.  See if we hit an arrow part... */
  719.         delta = hiliteOn = 0;
  720.         capRect = ctlRect;
  721.         capRect.bottom = ctlRect.top + kCapHeight;
  722.  
  723.         if (max > 1000)
  724.             delta = max/1000;
  725.         else delta = 1;
  726.  
  727.         if (PtInRect(origMouseLoc, &capRect)) {
  728.             delta = -delta;
  729.             hiliteCap = 0;
  730.         }
  731.         else {
  732.             capRect = ctlRect;
  733.             capRect.top = ctlRect.bottom - kCapHeight + 1;
  734.             if (PtInRect(origMouseLoc, &capRect)) {
  735.                 hiliteCap = 1;
  736.             }
  737.         }
  738.  
  739.         if (delta) {    /* We hit an arrow, and there is a change to do... */
  740.             do {
  741.  
  742.                 TrackAndRun(&mouseLoc);
  743.                 SetClip(clipRgn);
  744.                 if (PtInRect(mouseLoc, &capRect)) {        /* Still in arrow... */
  745.                     val = (*mMacControlH)->contrlValue + delta;
  746.                     if (val < 0) val = 0;
  747.                     if (val > max) val = max;
  748.                     
  749.                     if (val != (*mMacControlH)->contrlValue) {    /* Still scrolling... */
  750.                         hiliteOn = true;
  751.                         (*mMacControlH)->contrlValue = val;
  752.                         UpdateSlider(hiliteCap);
  753.                         SetClip(origClipRgn);
  754.                         SliderAction(val);
  755.                     }
  756.                     else {        /* Scrolled as far as we can go, so unhilite arrow. */
  757.                         if (hiliteOn) {
  758.                             UpdateSlider();
  759.                             hiliteOn = false;
  760.                         }
  761.                     }
  762.                 }
  763.                 else {        /* Outside arrow, so unhilite it. */
  764.                     if (hiliteOn) {
  765.                         UpdateSlider();
  766.                         hiliteOn = false;
  767.                     }
  768.                 }
  769.  
  770.                 while ((StillDown()) && (origTick + 20 > TickCount())) {};
  771.                     /* Don't go too fast. */
  772.  
  773.             } while (StillDown());
  774.         }
  775.     }
  776.  
  777.     SetClip(origClipRgn);
  778.     DisposeRgn(workRgn);
  779.     DisposeRgn(clipRgn);
  780.     DisposeRgn(origClipRgn);
  781.  
  782.     UpdateSlider();
  783.         /* Snap the slider to a move position.  The user may have let go of the
  784.         ** slider at a position that doesn't map exactly to the game position. */
  785.  
  786.     for (i = 2; i < 5; i++) KillCIcon(icons[i]);
  787.  
  788.     SetPort(oldPort);
  789. }
  790.  
  791.  
  792.  
  793. /*****************************************************************************/
  794.  
  795.  
  796.  
  797. void    
  798. CSliderControl::SliderAction(short /*newPos*/)
  799. {
  800. }
  801.  
  802.  
  803.  
  804.